

#include <dlfcn.h>
//#include <sys/types.h>
#include <errno.h>
#include "main.h"
#include "lst.h"
#include "ptrace.h"

extern int errno;

#define SCAN_MAX_MEM_SIZE 0XFFFFFFFF

#define WIFSTOPPED(w)	(((w)&0377) == 0177)

// The iPhone SDK doesn't have <sys/ptrace.h>, but it does have ptrace, and it 
// works just fine. 
typedef long (*ptrace_fp)(int _request, int _pid, void* _addr, long _data); 
ptrace_fp ptrace_p = NULL;
void* ptrace_handle = NULL;
 
hh_bool hh_load_ptrace() 
{ 
	ptrace_handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW); 
	if (ptrace_handle == NULL)
	{
	    APP_DEBUG_PRINT("load ptrace dll failed!\n");
		return FALSE;
	}
    ptrace_p = dlsym(ptrace_handle, "ptrace"); 
	if (ptrace_p == NULL)
	{
	    APP_DEBUG_PRINT("load ptrace func failed!\n");
	    return FALSE;
	}
	
	return TRUE;
} 

void hh_unload_ptrace()
{
    dlclose(ptrace_handle); 
}


typedef struct mem_info_struct mem_info_t;
struct mem_info_struct
{
	void* addr;
	ulint size;
	
	HH_LIST_NODE_T(mem_info_t) link;
};


typedef struct mem_lst_struct mem_lst_t;
struct mem_lst_struct
{
	HH_LIST_BASE_NODE_T(mem_info_t) mem_info_lst;
};

mem_lst_t g_mem_info;


typedef struct scan_addr_struct scan_addr_t;
struct scan_addr_struct
{
    void* addr;
	union 
	{
	    byte   i1;
		sint   i2;
		lint   i4;
		float  f;
		double d;
	}value;
	
	HH_LIST_NODE_T(scan_addr_t) link;
};

typedef struct scan_addr_lst_struct scan_addr_lst_t;
struct scan_addr_lst_struct
{
    HH_LIST_BASE_NODE_T(scan_addr_t) scan_addr_lst;
};

scan_addr_lst_t g_scan_addr;


ulint	g_page_size;

void
err_none()
{
    int i;
	i = 0;
}	
	
extern void* malloc(ulint);

void* hh_malloc(ulint size)
{
    void* p;
	
	p = (void*)(malloc(size));
    
	return p;
}

void hh_free(void* p)
{
    free(p);
}

hh_bool hh_attach(int pid)
{
    int status;

    /* attach, to the pid application, which should cause a SIGSTOP */
    if (ptrace_p(PTRACE_ATTACH, pid, NULL, 0) == -1L) {
        APP_DEBUG_PRINT("failed to attach to %d, %s\n", pid, strerror(errno));
        return FALSE;
    }

    /* wait for the SIGSTOP to take place. */
    if ((waitpid(pid, &status, 0) == -1 || !WIFSTOPPED(status)) && errno != EINTR) 
	{
        APP_DEBUG_PRINT("there was an error waiting for the pid to stop.\n");
        APP_DEBUG_PRINT("%s\n", strerror(errno));
        return FALSE;
    }

    /* everything looks okay */
    return TRUE;

}

hh_bool hh_detach(int pid)
{
    // addr is ignore under Linux, but should be 1 under FreeBSD in order to let the child process continue at what it had been interrupted
    return ptrace_p(PTRACE_DETACH, pid, (void*)1, 0) == 0;
}

hh_bool hh_peek_data(int pid, void* addr, long* value)
{
    long peek_data;
    
	/*
	peek_data = ptrace_p(PTRACE_PEEKDATA, pid, addr, 0);
	if (errno == 0) //success
	{
	    APP_DEBUG_PRINT("hh_peek_data success, addr=0X%08X ,errno = %d\n", addr, errno);
	    *value = peek_data;
		return TRUE;
	}
	else
	{
		APP_DEBUG_PRINT("hh_peek_data failed, pid=%d,addr=0X%08X ,value = %d, errno=%d, err=%s\n", pid, addr, peek_data, errno, strerror(errno));
	    *value = (long)errno;
		return FALSE;
	}
	*/
	
    struct ptrace_io_desc piod;

    /* NOTE: We assume that there are no distinct address spaces for
       instruction and data.  */
    //piod.piod_op = write ? PIOD_WRITE_D : PIOD_READ_D;
	piod.piod_op = PIOD_READ_D;
    piod.piod_offs = (void *) addr;
    piod.piod_addr = value;
    piod.piod_len = sizeof(long);

    if (ptrace (PT_IO, pid, &piod, 0) == -1)
      {
	/* If the PT_IO request is somehow not supported, fallback on
           using PT_WRITE_D/PT_READ_D.  Otherwise we will return zero
           to indicate failure.  */
	if (errno != EINVAL)
	  return 0;
      }
    else
      {
	/* Return the actual number of bytes read or written.  */
	return piod.piod_len;
      }	
}

hh_bool hh_poke_data(int pid, void* addr, long value)
{  
	long ret;
	
	ret = ptrace_p(PTRACE_POKETEXT, pid, addr, value);
	if (ret == -1)
	{
	    //APP_DEBUG_PRINT("hh_poke_data failed, addr=0X%08X, errno = %d\n", addr, errno);
	    return FALSE;
	}
	else
	{
	    return TRUE;
	}
}


lint hh_mem_page_size()
{
	return getpagesize();
}

hh_bool
hh_scan_init(int pid)
{
    ulint i = 0;
	long value;
	hh_bool bret;
	hh_bool is_first_page = TRUE;
	mem_info_t* mem_info = NULL;
	ulint n;
	void* addr;
	
	g_page_size = hh_mem_page_size();
	
	HH_LIST_INIT(g_mem_info.mem_info_lst);

	n = SCAN_MAX_MEM_SIZE / g_page_size + 1;
	for (i = 0; i < n; i++)
	{		
		addr = (void*)(i * g_page_size);
		bret = hh_peek_data(pid, addr, &value);
		if (bret)
		{
		    if (is_first_page)
			{
			    is_first_page = FALSE;
				
			    mem_info = hh_malloc(sizeof(mem_info_t));
				mem_info->addr = addr;
				mem_info->size = g_page_size;
				
                HH_LIST_ADD_LAST(link, g_mem_info.mem_info_lst, mem_info);
			}
			else
			{
				HH_ASSERT((byte*)mem_info->addr + mem_info->size == addr);
				mem_info->size += g_page_size;
			}
		}
		else
		{
			is_first_page = TRUE;
		}
	}
}

void
hh_first_scan(int pid)
{
    HH_LIST_INIT(g_scan_addr.scan_addr_lst);
}



void
hh_print_mem_info_lst()
{
    mem_info_t* mem_info = NULL;
	ulint i = 0;
	
	mem_info = HH_LIST_GET_FIRST(g_mem_info.mem_info_lst);
	while (mem_info != NULL)
	{
	    APP_DEBUG_PRINT("[%d]:addr = 0X%08X, size=%d, end=0X%08X\n", i, mem_info->addr, mem_info->size, ((byte*)mem_info->addr + mem_info->size));
		
		mem_info = HH_LIST_GET_NEXT(link, mem_info);
		i++;
	}
}

int main(int argc, char* argv[])
{
    lint pid;
	hh_bool bret;
	lint i;

	if (argc != 2)
	{
        APP_DEBUG_PRINT("need pid para\n");
		return -1;
    }
		
	pid = atoi(argv[1]);
	
	APP_DEBUG_PRINT("hh_load_ptrace \n");
	bret = hh_load_ptrace();
	if (!bret)
	{
	    APP_DEBUG_PRINT("hh_load_ptrace failed \n");
		return -1;
	}
	
	APP_DEBUG_PRINT("hh_attach \n");
	bret = hh_attach(pid);
	if (!bret)
	{
	    APP_DEBUG_PRINT("hh_attach failed \n");
		return -1;
	}	

	//for (i = 0; i < 6; i++)
	{
	    long peek_data = 888;
		
	    hh_peek_data(pid, (void*)0X0010FD64, &peek_data);
	    APP_DEBUG_PRINT("peek_data %d, errno=%d, %s\n", peek_data, errno, strerror(errno));
	}
	
	/*
	APP_DEBUG_PRINT("hh_scan_init \n");
	hh_scan_init(pid);
	
	APP_DEBUG_PRINT("hh_print_mem_info_lst \n");
	hh_print_mem_info_lst();
	*/
	
	hh_detach(pid);
	
	hh_unload_ptrace();
}
